﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using System.IO;
using Assembler_Compiler.IntelliSense;
using Assembler_Compiler.Properties;

namespace Assembler_Compiler
{
    public partial class Form1 : Form
    {
        private string filePath = string.Empty;
        private bool isCurrentFileSaved = true;
        private bool isClosing = false;
        private Compiler compiler = new Compiler();
        private CodeHighlight codeHighliter;
        
        public Form1()
        {
            InitializeComponent();

            this.codeTextBox.TextChanged += this.CodeTextBoxTextChanged;
            

            this.codeHighliter = new CodeHighlight(this.codeTextBox);
            DataGridViewImageColumn img = new DataGridViewImageColumn();
            img.Image = (Image)Resources.ResourceManager.GetObject("error");
            img.Width = 20;
            img.MinimumWidth = 20;
            img.Resizable = DataGridViewTriState.False;
            errorGridView.Columns.Insert(0, img);
        }

        private void SaveCurrentFile()
        {
            if (this.filePath != string.Empty)
            {
                if (this.isCurrentFileSaved == false)
                {
                    StreamWriter  stream = new StreamWriter(this.filePath, false);
                    stream.Write(this.codeTextBox.Text);
                    stream.Close();
                    stream.Dispose();
                    this.Text = Path.GetFileName(this.filePath);
                    this.isCurrentFileSaved = true;
                }
            }
        }
        
        // runs SaveFileDialog
        private void SaveFileAsClick(object sender, EventArgs e)
        {
            SaveFileDialog dlg = new SaveFileDialog();
            dlg.CheckPathExists = false;
            dlg.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
            DialogResult res = dlg.ShowDialog();
            if (res == DialogResult.OK)
            {
                this.filePath = dlg.FileName;
                this.Text = Path.GetFileName(this.filePath);
                this.isCurrentFileSaved = true;
                StreamWriter stream = new StreamWriter(this.filePath, false);
                stream.Write(this.codeTextBox.Text);
                stream.Close();
                stream.Dispose();
            }
        }

        // offers to save file if necessary
        private bool Exit()
        {
            if (!this.isCurrentFileSaved)
            {
                DialogResult res = MessageBox.Show("Do you want to save file befor exit?", "File not saved!", MessageBoxButtons.YesNoCancel);
                if (res == DialogResult.Yes)
                {
                    this.SaveCurrentFile();
                    return true;
                }

                if (res == DialogResult.No)
                {
                    return true;
                }

                if (res == DialogResult.Cancel)
                {
                    return false;
                }
            }
            else
            {
                return true;
            }

            return true;
        }

        private void Form1FormClosing(object sender, FormClosingEventArgs e)
        {
            if (!this.isClosing)
            {
                e.Cancel = !this.Exit();
            }
        }

        private void CodeTextBoxTextChanged(object sender, EventArgs e)
        {
            if (this.isCurrentFileSaved)
            {
                this.isCurrentFileSaved = false;
                this.Text += "*";
            }
        }

        #region tool strip menu event handlers

        private void NewFileToolStripMenuItemClick(object sender, EventArgs e)
        {
            this.SaveCurrentFile();

            this.filePath = Application.ExecutablePath.TrimEnd(Path.GetFileName(Application.ExecutablePath).ToCharArray());
            int i = 1;
            while (File.Exists(this.filePath + @"NewFile" + i + @".txt"))
            {
                i++;
            }

            this.filePath += @"NewFile" + i + @".txt";
            File.Create(this.filePath).Close();
            this.isCurrentFileSaved = true;
            this.Text = @"NewFile" + i + @".txt";
            this.codeTextBox.Enabled = true;
            this.codeTextBox.Text = string.Empty;
        }

        private void GenerateExample1Click(object sender, EventArgs e)
        {
            String str = "ORG 100H; резерв для PSP-префікса прогр. сег.\n" +
                "BEGIN: JMP MAINPROG; обхід даних\n" +
                "MESS1 DB 'Testing: print a line:'\n" +
                "DB '$'\n" +
                "MAINPROG PROC NEAR; процедура ближнього типу\n" +
                "MOV AH,09; вивести на екран рядок до '$'\n" +
                "LEA DX,MESS1;адреса рядка, який виводимо\n" +
                "INT 21H\n" +
                "EXIT: MOV AX,4C00h; функція 4C (76)\n" +
                "INT 21h;'припинити програму'\n" +
                "MAINPROG ENDP\n";
            codeTextBox.Text = str;
            this.codeHighliter.HighLightAll();
        }

        private void GenerateExample2Click(object sender, EventArgs e)
        {
            String str = "ORG 100H\n" +
                "BEGIN: MOV AX,AB\n" +
                "MOV BX,ZN\n" +
                "XCHG AX,BX\n" +
                "MOV PRINT,AX; знаки +<\n" +
                "LEA DX,PRINT; адреса рядка\n" +
                "MOV AH,9; функція DOS - вивести рядок на екран\n" +
                "INT 21H; виклик функції\n" +
                "XCHG BH,BL; поміняти місцями букви\n" +
                "MOV PRINT,BX; букви BA\n" +
                "LEA DX,PRINT\n" +
                "MOV AH,9\n" +
                "INT 21H\n" +
                "RET; кінець програми\n" +
                "AB DW 4241H; коди літер A(41H), B(42H) в оберненому порядку\n" +
                "ZN DW 3C2BH; коди знаків +(2BH),<(3CH) в оберненому порядку\n" +
                "PRINT DW ?; дві літери для друкування\n" +
                "DB 13d; перехід на новий рядок - 13,10\n" +
                "DB 10d\n" +
                "DB '$'; обмежувач рядка\n" +
                "; мають бути надруковані на екрані два рядки: '+<' і 'BA'\n" +
                "CODESG ENDS\n" +
                "END BEGIN\n";
            codeTextBox.Text = str;
            this.codeHighliter.HighLightAll();
        }

        private void CompileClick(object sender, EventArgs e)
        {
            if (this.filePath != string.Empty)
            {
                try
                {
                    this.SaveCurrentFile();
                    this.compiler.Compile(this.filePath);
                }
                catch (Exception exception)
                {
                    MessageBox.Show(exception.Message ,"Exception",MessageBoxButtons.OK,MessageBoxIcon.Error);
                }

                if (ErrorList.Count == 0)
                {
                    countErrorsLabel.Text = "There are no errors!";
                    countErrorsLabel.Visible = false;
                    errorImagePanel.Visible = false;
                    errorGridView.Visible = false;
                    codeTextBox.Height = 505;
                    codeTextBox.Anchor = (AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom);
                }
                else
                {
                    this.errorGridResizing();
                    countErrorsLabel.Text = "There are " + ErrorList.Count.ToString() +" error" +((ErrorList.Count==1)?"":"s")+ "!";
                    countErrorsLabel.Visible = true;
                    errorImagePanel.Visible = true;
                    errorGridView.Visible = true;
                    codeTextBox.Height = 333;
                    codeTextBox.Anchor = (AnchorStyles.Top|AnchorStyles.Left|AnchorStyles.Right) ;
                    errorGridView.Rows.Clear();
                    for (int i = 0; i < ErrorList.Count; ++i)
                    {
                        Error curr = ErrorList.GetError(i);
                        errorGridView.Rows.Add();
                        errorGridView.Rows[i].Cells[1].Value = curr.Message;
                        errorGridView.Rows[i].Cells[2].Value = (curr.Line==-1)?"n/d":curr.Line.ToString();
                        errorGridView.Rows[i].Cells[3].Value = curr.Type.ToString();
                    }

                    this.HighlightErrors();
                }
            }
            else
            {
                MessageBox.Show("There are nothing to compile", "Tip", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        private void OpenFileClick(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                this.filePath = dlg.FileName;
                this.Text = Path.GetFileName(this.filePath);
                this.isCurrentFileSaved = true;
                this.codeTextBox.Enabled = true;
                this.codeTextBox.Text = File.ReadAllText(this.filePath);
            }
        }

        private void SaveFileClick(object sender, EventArgs e)
        {
            this.SaveCurrentFile();
        }

        private void ExitClick(object sender, EventArgs e)
        {
            if (this.Exit())
            {
                this.isClosing = true;
                Application.Exit();
            }
        }

        #endregion

        private void errorGridResizing()
        {
            int fullWidth = 0;
            foreach (DataGridViewColumn col in errorGridView.Columns)
            {
                if (col.Name != "Description")
                {
                    fullWidth += col.Width;
                }
            }

            errorGridView.Columns["Description"].Width = errorGridView.Width - fullWidth - 8;
        }

        private void errorGridView_Resize(object sender, EventArgs e)
        {
            this.errorGridResizing();
        }

        private void errorGridView_ColumnWidthChanged(object sender, DataGridViewColumnEventArgs e)
        {
            this.errorGridResizing();
        }

        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Control && e.KeyCode == Keys.S)       // Ctrl+S Save
            {
                this.SaveCurrentFile();
                e.SuppressKeyPress = true;  // stops bing! also sets handeled which stop event bubbling
            }
            if (e.Control && e.KeyCode == Keys.O)       // Ctrl+O Open
            {
                this.OpenFileClick(sender,e);
                e.SuppressKeyPress = true;  // stops bing! also sets handeled which stop event bubbling
            }
            if (e.Control && e.KeyCode == Keys.N)       // Ctrl+N New
            {
                this.NewFileToolStripMenuItemClick(sender,e);
                e.SuppressKeyPress = true;  // stops bing! also sets handeled which stop event bubbling
            }
            if (e.Control && e.KeyCode == Keys.F5)       // Ctrl+F5  Compile
            {
                this.CompileClick(sender,e);
                e.SuppressKeyPress = true;  // stops bing! also sets handeled which stop event bubbling
            }
            if (e.Control && e.KeyCode == Keys.F9)      // Ctrl+F9 Compile
            {
                this.CompileClick(sender, e);
                e.SuppressKeyPress = true;  // stops bing! also sets handeled which stop event bubbling
            }
        }

        private void errorGridView_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
        {
            int lineNumber = int.Parse(this.errorGridView.Rows[e.RowIndex].Cells[2].Value.ToString()) - 1;
            int selectionStart = this.codeTextBox.GetFirstCharIndexFromLine(lineNumber);
            this.codeTextBox.SelectionLength = 0;
            this.codeTextBox.SelectionStart = selectionStart + 1;
            //this.codeTextBox.Select(selectionStart, 0);
            this.codeTextBox.ScrollToCaret();
        }

        private void HighlightErrors()
        {
            for (int i = 0; i < this.errorGridView.Rows.Count; i++ )
            {
                int lineNumber = int.Parse(this.errorGridView.Rows[i].Cells[2].Value.ToString()) - 1;
                int selectionStart = this.codeTextBox.GetFirstCharIndexFromLine(lineNumber);
                this.codeTextBox.Select(selectionStart, this.codeTextBox.Lines[lineNumber].Length);
                this.codeTextBox.SelectionColor = Color.Red;
            }
        }
    }
}
